package beans;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.annotation.PostConstruct;
import javax.ejb.EJB;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.SessionScoped;
import javax.faces.context.FacesContext;

import utils.TechresourceDAORemote;
import dtos.TechresourceDTO;
import dtos.UserDTO;

/**
 * 
 * Java bean that allows us to see all tech resources, and there current
 * availability, from the current users domain. The current user status can also
 * be changed.
 * 
 * @author Szekely Krisztian-Vilmos
 * 
 */
@ManagedBean(name = "statusBean")
@SessionScoped
public class StatusBean {
	@EJB
	TechresourceDAORemote tr;

	private UserDTO user;

	@ManagedProperty(value = "#{onlineUsers}")
	private OnlineUsers ou;

	private List<TechresourceDTO> listResource;
	private List<ResourceUser> listResourceUser;

	@PostConstruct
	private void init() {
		user = (UserDTO) FacesContext.getCurrentInstance().getExternalContext()
				.getSessionMap().get("loggedUser");
		refresh();
	}

	public UserDTO getUser() {
		return user;
	}

	public void setUser(UserDTO user) {
		this.user = user;
	}

	public OnlineUsers getOu() {
		return ou;
	}

	public void setOu(OnlineUsers ou) {
		this.ou = ou;
	}

	public List<TechresourceDTO> getListResource() {
		return listResource;
	}

	public void setListResource(List<TechresourceDTO> listResource) {
		this.listResource = listResource;
	}

	public List<ResourceUser> getListResourceUser() {
		return listResourceUser;
	}

	public void setListResourceUser(List<ResourceUser> listResourceUser) {
		this.listResourceUser = listResourceUser;
	}

	/**
	 * Update the status of the user in the map of online users from bean
	 * {@link OnlineUsers}.
	 */
	public void setOnlineStatus() {
		UserDTO tmp = ou.getOnlineMap().get(user.getIp());
		tmp.setStatus(user.getStatus());
		ou.addUserToMonitor(tmp);
		FacesContext.getCurrentInstance().getExternalContext().getSessionMap()
				.put("loggedUser", user);
	}

	/**
	 * A list of all the tech resources from the users domain is retrieved, and
	 * each device is pinged.
	 */
	public void performPings() {
		listResource = tr.getTechResourcesDTOByDomainId(user.getIdDomain());
		for (TechresourceDTO resource : listResource) {
			resource.setStatus(ping(resource.getIp()));
		}
	}

	/**
	 * Performs a ping test on the tech resource with the given ip address. The
	 * returned status can be 'online' or 'offline'.
	 * 
	 * @param ip
	 *            - the ip address of the tech resource to be tested
	 * @return A String with the status of the tech resource that was tested.
	 */
	private String ping(String ip) {
		Pattern pattern = Pattern.compile("(.*\\()(\\d{1,3})(%.*)");
		Matcher matcher;
		int percent;

		try {
			Process process = Runtime.getRuntime().exec(
					"ping -n 3 -w 1000 " + ip);
			BufferedReader in = new BufferedReader(new InputStreamReader(
					process.getInputStream()));
			String inputLine;

			while ((inputLine = in.readLine()) != null) {
				System.out.println(inputLine);
				matcher = pattern.matcher(inputLine);
				if (matcher.find()) {
					percent = Integer.parseInt(matcher.group(2));
					if (percent > 0) {
						return "offline";
					}
				}
			}
			in.close();
		} catch (Exception ex) {
			return "offline";
		}

		return "online";
	}

	/**
	 * Refresh the list of tech resources, ping then all, and rebuild the list
	 * that binds online users to tech resources.
	 */
	public void refresh() {
		performPings();
		bindUserToResource();
	}

	/**
	 * Populate the list of resource-user pairs. The list of resources is
	 * iterated, and if a user is found occupying the resource a
	 * {@link ResourceUser} instance is added to the list, if the resource is
	 * not in use by a user it is either an offline resource, or a printer,
	 * which are marked correspondingly and added to the list.
	 */
	private void bindUserToResource() {
		listResourceUser = new ArrayList<>();

		Map<String, UserDTO> map = ou.getOnlineMap();
		for (TechresourceDTO res : listResource) {
			UserDTO usr = map.get(res.getIp());

			if ((usr != null) && (usr.getIdDomain() == user.getIdDomain())) {
				listResourceUser.add(new ResourceUser(res, usr));
			} else {
				Pattern pattern = Pattern.compile("^printer.*$",
						Pattern.CASE_INSENSITIVE);
				Matcher matcher = pattern.matcher(res.getName());

				if (matcher.matches()) {
					listResourceUser.add(new ResourceUser(res, "yes"));
				} else {
					listResourceUser.add(new ResourceUser(res, "no"));
				}
			}
		}
	}

	/**
	 * Inner class used to build a list of recource-user pairs.
	 * 
	 * @author Szekely Krisztian-Vilmos
	 * 
	 */
	public class ResourceUser {
		private TechresourceDTO res;
		private UserDTO user;
		private String isPrinter;

		/**
		 * Create a ResourceUser instance that has no user. This means that the
		 * resource is offline, or it is a printer.
		 * 
		 * @param res
		 *            - the tech resource
		 * @param isPrinter
		 *            - is the tech resource a printer or just an offline device
		 */
		public ResourceUser(TechresourceDTO res, String isPrinter) {
			this.res = res;
			this.user = null;
			this.isPrinter = isPrinter;
		}

		/**
		 * Create a ResourceUser instance that is in use by an online user.
		 * 
		 * @param res
		 *            -the tech resource
		 * @param user
		 *            - the user using the tech resource
		 */
		public ResourceUser(TechresourceDTO res, UserDTO user) {
			this.res = res;
			this.user = user;
			isPrinter = "no";
		}

		public TechresourceDTO getRes() {
			return res;
		}

		public void setRes(TechresourceDTO res) {
			this.res = res;
		}

		public UserDTO getUser() {
			return user;
		}

		public void setUser(UserDTO user) {
			this.user = user;
		}

		public String getIsPrinter() {
			return isPrinter;
		}

		public void setIsPrinter(String isPrinter) {
			this.isPrinter = isPrinter;
		}
	}
}
